package com.vidmt.api.modules.sys.api;

import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;

import org.dom4j.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.thinkgem.jeesite.modules.sys.utils.UserUtils;
import com.vidmt.api.common.exceptions.CodeException;
import com.vidmt.api.common.pojo.ApiResultServer;
import com.vidmt.api.common.pojo.KV;
import com.vidmt.api.common.utils.CommUtil;
import com.vidmt.api.common.utils.StrUtil;
import com.vidmt.api.common.utils.XmlD4jUtil;
import com.vidmt.api.common.web.ApiController;
import com.vidmt.api.modules.sys.Enums.LvlType;
import com.vidmt.api.modules.sys.Enums.OrderStatus;
import com.vidmt.api.modules.sys.Enums.PayType;
import com.vidmt.api.modules.sys.entity.Order;
import com.vidmt.api.modules.sys.entity.Role;
import com.vidmt.api.modules.sys.entity.User;
import com.vidmt.api.modules.sys.entity.sub.AliOrder;
import com.vidmt.api.modules.sys.entity.sub.IapOrder;
import com.vidmt.api.modules.sys.entity.sub.RoleLvl;
import com.vidmt.api.modules.sys.entity.sub.WxOrder;
import com.vidmt.api.modules.sys.pay.ali.AlipayConfig;
import com.vidmt.api.modules.sys.pay.ali.AlipayNotify;
import com.vidmt.api.modules.sys.pay.ali.AlipayUtil;
import com.vidmt.api.modules.sys.pay.wx.WxHttps;
import com.vidmt.api.modules.sys.pay.wx.WxPayConfig;
import com.vidmt.api.modules.sys.pay.wx.WxPayUtil;
import com.vidmt.api.modules.sys.service.InfoService;
import com.vidmt.api.modules.sys.service.OrderService;
import com.vidmt.api.modules.sys.service.RoleService;

@Controller
@ResponseBody
@RequestMapping("/api/1/pay")
public class ApiPayController extends ApiController {
	private static final Logger log = LoggerFactory.getLogger(ApiPayController.class);
	private static final String ALI_SUCCESS = "success";
	private static final String ALI_FAIL = "fail";
	private static final String WX_SUCCESS = "<xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>";
	private static final String WX_FAIL = "<xml><return_code>FAIL</return_code><return_msg>NOT OK</return_msg></xml>";

	@Autowired
	private OrderService orderService;

	@Autowired
	private RoleService roleService;

	@Autowired
	private InfoService infoService;

	
	@RequestMapping("/ali/getpayinfo.*")
	public ApiResultServer getAlipayOrderInfo(LvlType lvltype) {
		User user = UserUtils.getCurrentUser();
		long uid = user.getId();
		Role role = roleService.getRoleByKey(lvltype.name());
		RoleLvl lvl = new RoleLvl(role);
		AliOrder order = new AliOrder();
		order.genId();
		order.setLvlType(lvltype);
		order.setSkus(new String[] { lvltype.name() });
		order.setPayType(PayType.ALI);
		order.setStatus(OrderStatus.INIT);

		String prefix = String.format("%s%s", uid, order.getId());
		order.setSubject(Order.genSubject(lvl) + prefix);

		order.setTotalFee(lvl.getMoney());
		order.setUid(uid);
		order.encodeAttach();
		JSONObject payinfo = order.toPayinfo();
		List<KV> list = new ArrayList<>();
		for (Entry<String, Object> entry : payinfo.entrySet()) {
			list.add(new KV(entry.getKey(), (String) entry.getValue()));
		}

		orderService.save(order);
		return new ApiResultServer(AlipayUtil.toParamString(list));
	}

	@RequestMapping("/wx/getpayinfo.*")
	public ApiResultServer getWxpayOrderInfo(LvlType lvltype) {
		User user = UserUtils.getCurrentUser();
		long uid = user.getId();
		Role role = roleService.getRoleByKey(lvltype.name());
		RoleLvl lvl = new RoleLvl(role);
		WxOrder order = new WxOrder();
		order.genId();
		order.setLvlType(lvltype);
		order.setSkus(new String[] { lvltype.name() });
		order.setStatus(OrderStatus.INIT);
		order.setPayType(PayType.WX);
		String prefix = String.format("%s%s", uid, order.getId());
		order.setSubject(Order.genSubject(lvl) + prefix);
		order.setTotalFee(lvl.getMoney());
		order.setUid(uid);
		order.encodeAttach();
		orderService.save(order);
		return new ApiResultServer(order.toPayinfo());
	}

	@RequestMapping("/iap/getpayinfo.*")
	public ApiResultServer getAppayOrderInfo(LvlType lvltype) {
		User user = UserUtils.getCurrentUser();
		long uid = user.getId();
		Role role = roleService.getRoleByKey(lvltype.name());
		RoleLvl lvl = new RoleLvl(role);

		IapOrder order = new IapOrder();
		order.genId();
		order.setLvlType(lvltype);
		order.setSkus(new String[] { lvltype.name() });
		order.setStatus(OrderStatus.INIT);

		String prefix = String.format("%s%s", uid, order.getId());
		order.setSubject(Order.genSubject(lvl) + prefix);

		order.setTotalFee(lvl.getIosMoney());
		order.setUid(uid);
		order.encodeAttach();
		orderService.save(order);
		return new ApiResultServer(order.toPayinfo());
	}

	@SuppressWarnings("deprecation")
	@RequestMapping("/public/ali/notify.api")
	public String alipayNotify(String trade_no, String trade_status, String refund_status, HttpServletRequest req)
			throws IOException {
		// 获取支付宝POST过来反馈信息
		// 若未通知成功，一般情况下，25小时以内完成 8 次通知（通知的间隔频率一般是：2m,10m,10m,1h,2h,6h,15h）
		Map<String, String> params = new HashMap<String, String>();
		Map<String, String[]> requestParams = req.getParameterMap();
		for (String name : requestParams.keySet()) {
			// String name = iter.next();
			String[] values = (String[]) requestParams.get(name);
			String valueStr = "";
			for (int i = 0; i < values.length; i++) {
				valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
			}
			// 乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
			// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
			params.put(name, valueStr);
		}

		if (AlipayNotify.verify(params)) {// 验证成功
			if (refund_status == null && !"TRADE_SUCCESS".equals(trade_status)) {
				if (!"WAIT_BUYER_PAY".equals(trade_status)) {
					log.info("支付宝通知无效,trade_no:{},refund_status:{}/trade_status:{}", trade_no, refund_status,
							trade_status);
				}
				return ALI_SUCCESS;
			}
			// ////////////////////////////////////////////////////////////////////////////////////////
			// 获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
			// 商品描述信息
			String orderid = params.get("out_trade_no");
			Order order = orderService.load(orderid);
			if (order == null) {
				log.error("order not exist:{}", orderid);
				return ALI_SUCCESS;
			}
			// 交易金额
			// 买家支付宝账号
			String buyer_email = params.get("buyer_email");
			// 交易付款时间
			String gmt_payment = params.get("gmt_payment");
			Date payTime = AlipayUtil.parseDate(gmt_payment);

			order.setPayAcc(buyer_email);
			order.setTradeNo(trade_no);
			order.setPayTime(payTime);
			order.setStatus(OrderStatus.PAYED);
			if (refund_status != null) {
				if ("REFUND_SUCCESS".equals(refund_status)) {
					if (order.getStatus() != OrderStatus.PAYED) {
						log.error("order cant be refund as status is:{}", order.getStatus());
					} else if ("TRADE_SUCCESS".equals(trade_status)) {
						orderService.refund(order, JSON.toJSONString(params));
					} else if ("TRADE_CLOSED".equals(trade_status)) {
						orderService.refund(order, JSON.toJSONString(params));
					}
				} else {
					log.warn("支付宝通知无效,trade_no:{},refund_status:{}", trade_no, refund_status);
				}
				return ALI_SUCCESS;
			}
			String sellerEmail = params.get("seller_email");
			if (!AlipayConfig.SELELR_ACCOUNT.equals(sellerEmail)) {// 服务器端验证
				log.warn("支付宝商户EMAIL错误:trade_no:{},{}", trade_no, sellerEmail);
				return ALI_SUCCESS;
			}
			if (!"TRADE_SUCCESS".equals(trade_status)) {
				if (!"TRADE_FINISHED".equals(trade_status)) {
					log.info("支付宝通知无效,trade_no:{},trade_status:{}", trade_no, trade_status);
				}
				return ALI_SUCCESS;
			}
			// 请在这里加上商户的业务逻辑程序代码
			// ——请根据您的业务逻辑来编写程序（以下代码仅作参考）——
			if (trade_status.equals("TRADE_FINISHED")) {
				// 判断该笔订单是否在商户网站中已经做过处理
				// 如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
				// 如果有做过处理，不执行商户的业务程序
				// 注意：
				// 该种交易状态只在两种情况下出现
				// 1、开通了普通即时到账，买家付款成功后。
				// 2、开通了高级即时到账，从该笔交易成功时间算起，过了签约时的可退款时限（如：三个月以内可退款、一年以内可退款等）后。
				log.info("调试:TRADE_FINISHED:{}", JSON.toJSONString(params));
				return ALI_SUCCESS;// 此处因为之前已经处理，此处不会执行
			} else if (trade_status.equals("TRADE_SUCCESS")) {
				// 判断该笔订单是否在商户网站中已经做过处理
				// 如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
				// 如果有做过处理，不执行商户的业务程序
				// 注意：
				// 该种交易状态只在一种情况下出现——开通了高级即时到账，买家付款成功后。
				if (order.getStatus() != OrderStatus.INIT) {
					log.error("ALI order cant be payed as status is:{}", order.getStatus());
				} else {
					orderService.paysuccess(order, JSON.toJSONString(params));
					infoService.save(order.getUid(), order.getPayAcc());
				}
				log.debug("alipay success!");
				return ALI_SUCCESS;
			}
			return ALI_SUCCESS;
			// ——请根据您的业务逻辑来编写程序（以上代码仅作参考）——
			// ////////////////////////////////////////////////////////////////////////////////////////
		} else {// 验证失败
			log.warn("支付宝验证失败!trade_no:{}", trade_no);
			return ALI_FAIL;
		}
	}

	@RequestMapping("/public/wx/notify.api")
	public String wxpayNotify(HttpServletRequest req) {
		String xml = null;
		// 通知频率为15/15/30/180/1800/1800/1800/1800/3600，单位：秒(正确处理重复的通知)
		try {
			Document doc = XmlD4jUtil.getXmlDoc(new InputStreamReader(req.getInputStream(), "UTF-8"));
			xml = doc.asXML();
			Map<String, String> notifyMap = WxPayUtil.readXml(doc);
			// log.info("WX RCV:{}",JSON.toJSONString(notifyMap));

			if (!"SUCCESS".equals(notifyMap.get("return_code")) || !"SUCCESS".equals(notifyMap.get("result_code"))) {
				log.warn("微信支付通知无效：" + JSON.toJSONString(notifyMap));
				return WX_FAIL;
			}

			String appId = notifyMap.get("appid");
			String mchId = notifyMap.get("mch_id");
			if (!WxPayConfig.WXPAY_APP_ID.equals(appId) || !WxPayConfig.WXPAY_MERCHANT_ID.equals(mchId)) {// 服务器端验证
				log.warn("微信支付通知失败,appid 或mch_id错误：" + JSON.toJSONString(notifyMap));
				return WX_FAIL;
			}
			String outtradeNO = notifyMap.get("out_trade_no");
			Order order = orderService.load(outtradeNO);
			String tradeNo = notifyMap.get("transaction_id");
			String openid = notifyMap.get("openid");
			Date payTime = WxPayUtil.parseDate(notifyMap.get("time_end"));

			if (order.getStatus() != OrderStatus.INIT) {
				log.error("WX order cant be payed as status is:{}", order.getStatus());
			} else {
				order.setPayAcc(openid);
				order.setTradeNo(tradeNo);
				order.setPayTime(payTime);
				order.setStatus(OrderStatus.PAYED);

				orderService.paysuccess(order, JSON.toJSONString(xml));
				log.debug("wxpay success!");
			}
			return WX_SUCCESS;
		} catch (Throwable e) {
			log.error("微信支付失败未知原因:", e);
			log.error("微信支付失败，xml{}", xml);
			return WX_FAIL;
		}
	}

	@RequestMapping("/public/iap/notify.*")
	public ApiResultServer iapPayNotify(long uid, String out_trade_no, String trade_no, LvlType lvltype, String receipt,
			Boolean isSandebox, Date paytime) {
		Order oldOrder = orderService.load(out_trade_no);
		if (oldOrder == null) {
			boolean valid = true;
			if (receipt != null) {
				try {
					JSONObject entityJson = new JSONObject();
					entityJson.put("receipt-data", receipt);
					String url = (isSandebox ? "https://sandbox.itunes.apple.com/verifyReceipt"
							: "https://buy.itunes.apple.com/verifyReceipt");
					byte[] respbyte = WxHttps.httpPost(url, entityJson.toJSONString());
					if (respbyte == null) {
						throw new IOException("访问IOS服务器失败");
					}
					String content = StrUtil.newutf8(respbyte);
					JSONObject respJson = JSON.parseObject(content);
					int status = respJson.getIntValue("status");
					if (status == 21007) {
						valid = false;
						log.error("正式服务器的结果验证发送到IOS测试服务器上导致出错");
					} else if (status != 0) {
						valid = false;
						log.error("IOS服务器验证出错：{}", content);
					}
				} catch (Throwable e) {
					log.warn("IAP服务器访问出错", e);
				}
			}

			if (!valid) {
				return new ApiResultServer(new CodeException());
			}
			if (paytime == null) {
				paytime = new Date();
			}

			Order order = orderService.load(out_trade_no);
			order.setSkus(new String[] { lvltype.name() });

			String prefix = String.format("%s%s", uid, order.getId());
			Role role = roleService.getRoleByKey(lvltype.name());
			RoleLvl lvl = new RoleLvl(role);
			order.setSubject(Order.genSubject(lvl) + prefix);
			order.setTotalFee(lvl.getIosMoney());
			order.setPayType(PayType.IAP);
			order.setTradeNo(trade_no);
			order.setPayTime(paytime);
			order.setStatus(OrderStatus.PAYED);
			order.encodeAttach();
			order.setLvlType(lvltype);

			JSONObject json = new JSONObject();
			json.put("uid", uid);
			json.put("out_trade_no", out_trade_no);
			json.put("lvltype", lvltype);

			orderService.paysuccess(order, json.toJSONString());
		}

		return new ApiResultServer();
	}
}
